package com.cf.lock.core;

import com.cf.constant.EnumReturnCode;
import com.cf.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
public class ZDisLockAspect implements Ordered {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ZLockProperties zLockProperties;

    @Autowired
    private LockService lockService;


    public ZDisLockAspect() {
        log.info("实例化分布式锁管理切面！！！");
    }

    @Pointcut("@annotation(ZLock)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        Object[] ars = joinPoint.getArgs();

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        ZLock lock = method.getAnnotation(ZLock.class);

        String lockKey = lockService.getLockKey(method, ars);

        //key过期时间
        long liveTime = lock.invalidTime() * 1000L;

        //自旋等待时间
        long waitTime = lock.waitTime() * 1000L;

        boolean locked = false;
        try {

            locked = lockService.lockWait(lockKey, waitTime, liveTime);
            if (locked) {
                log.info("加锁成功：【{}】，锁有效时间到【{}】", lock, System.currentTimeMillis() + liveTime);
                return joinPoint.proceed();
            } else {
                throw new BizException(EnumReturnCode.ERROR.getCode(), "操作过于频繁，请稍后重试");
            }
        } finally {
            if (locked) {
                log.info("开始释放所：【{}】", lock);
                stringRedisTemplate.delete(lockKey);
            }
        }
    }

    @Override
    public int getOrder() {
        return zLockProperties.getOrder();
    }

}
